﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.Xml;

namespace Bing_Wallpaper_Downloader
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            ReportError.HandleError((Exception)e.ExceptionObject);
        }

        private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            ReportError.HandleError(e.Exception);
        }

        //Bing API AppID
        //http://www.bing.com/developers/createapp.aspx
        string appId = "9E954439354CC3134C69ECE2C7753C1B02C7F9AD";

        NotifyIcon notifyIcon;

        private string wallpaperFileLocation;
        private string wallpaperSearchFrequency;
        private string wallpaperSwitchFrequency;
        private string wallpaperSwitchMethod;
        private string wallpaperSwitchCount;
        private string desktopBackgroundStyle;
        private string translationLanguage;
        private bool loggingEnabled;

        private System.Windows.Forms.Timer wallpaperSearchTimer;
        private System.Windows.Forms.Timer wallpaperSwitchTimer;

        private int wallpaperSearchTimerInterval;
        private int wallpaperSwitchTimerInterval;

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Text = Application.ProductName;

            IconExtractor iconextractor = new IconExtractor();
            this.Icon = iconextractor.Extract(Application.ExecutablePath);

            ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
            contextMenuStrip.ShowImageMargin = false;
            contextMenuStrip.Items.Add("Exit");
            contextMenuStrip.ItemClicked += new ToolStripItemClickedEventHandler(contextMenuStrip_ItemClicked);

            notifyIcon = new NotifyIcon();
            notifyIcon.Visible = true;
            notifyIcon.Text = Application.ProductName;
            notifyIcon.Icon = this.Icon;
            notifyIcon.ContextMenuStrip = contextMenuStrip;
            notifyIcon.DoubleClick += new EventHandler(notifyIcon_DoubleClick);

            List<CultureInfo> languages = Translator.Languages(appId);
            foreach (CultureInfo language in languages)
            {
                comboBox6.Items.Add(language);
            }
            comboBox6.DisplayMember = "DisplayName";
            comboBox6.ValueMember = "Name";
            comboBox6.Items.Insert(0, "Disabled");

            wallpaperFileLocation = Properties.Settings.Default.wallpaperFileLocation;
            textBox1.Text = wallpaperFileLocation;

            wallpaperSearchFrequency = Properties.Settings.Default.wallpaperSearchFrequency;
            comboBox1.SelectedItem = wallpaperSearchFrequency;

            wallpaperSwitchFrequency = Properties.Settings.Default.wallpaperSwitchFrequency;
            comboBox2.SelectedItem = wallpaperSwitchFrequency;

            wallpaperSwitchMethod = Properties.Settings.Default.wallpaperSwitchMethod;
            comboBox3.SelectedItem = wallpaperSwitchMethod;

            wallpaperSwitchCount = Properties.Settings.Default.wallpaperSwitchCount;
            comboBox4.SelectedItem = wallpaperSwitchCount;

            desktopBackgroundStyle = Properties.Settings.Default.desktopBackgroundStyle;
            comboBox5.SelectedItem = desktopBackgroundStyle;

            translationLanguage = Properties.Settings.Default.translationLanguage;
            if (translationLanguage != "Disabled")
            {
                comboBox6.SelectedItem = new CultureInfo(translationLanguage);
            }
            else
            {
                comboBox6.SelectedItem = translationLanguage;
            }

            loggingEnabled = Convert.ToBoolean(ConfigurationManager.AppSettings["loggingEnabled"]);
            checkBox1.Checked = loggingEnabled;

            if (wallpaperSearchFrequency != "Disabled")
            {
                wallpaperSearchTimer = new System.Windows.Forms.Timer();
                switch (wallpaperSearchFrequency)
                {
                    case "1 minute":
                        wallpaperSearchTimerInterval = 1 * 60 * 1000;
                        break;
                    case "2 minutes":
                        wallpaperSearchTimerInterval = 2 * 60 * 1000;
                        break;
                    case "5 minutes":
                        wallpaperSearchTimerInterval = 5 * 60 * 1000;
                        break;
                    case "10 minutes":
                        wallpaperSearchTimerInterval = 10 * 60 * 1000;
                        break;
                    case "15 minutes":
                        wallpaperSearchTimerInterval = 15 * 60 * 1000;
                        break;
                    case "30 minutes":
                        wallpaperSearchTimerInterval = 30 * 60 * 1000;
                        break;
                    case "1 hour":
                        wallpaperSearchTimerInterval = 1 * 60 * 60 * 1000;
                        break;
                    case "2 hours":
                        wallpaperSearchTimerInterval = 2 * 60 * 60 * 1000;
                        break;
                    case "4 hours":
                        wallpaperSearchTimerInterval = 4 * 60 * 60 * 1000;
                        break;
                    case "6 hours":
                        wallpaperSearchTimerInterval = 6 * 60 * 60 * 1000;
                        break;
                    case "12 hours":
                        wallpaperSearchTimerInterval = 12 * 60 * 60 * 1000;
                        break;
                }
                wallpaperSearchTimer.Tick += new EventHandler(wallpaperSearchTimer_Tick);

                if (wallpaperSearchTimerInterval > 0)
                {
                    wallpaperSearchTimer.Enabled = true;
                }
            }

            if (wallpaperSwitchFrequency != "Disabled")
            {
                wallpaperSwitchTimer = new System.Windows.Forms.Timer();
                switch (wallpaperSwitchFrequency)
                {
                    case "1 minute":
                        wallpaperSwitchTimerInterval = 1 * 60 * 1000;
                        break;
                    case "2 minutes":
                        wallpaperSwitchTimerInterval = 2 * 60 * 1000;
                        break;
                    case "5 minutes":
                        wallpaperSwitchTimerInterval = 5 * 60 * 1000;
                        break;
                    case "10 minutes":
                        wallpaperSwitchTimerInterval = 10 * 60 * 1000;
                        break;
                    case "15 minutes":
                        wallpaperSwitchTimerInterval = 15 * 60 * 1000;
                        break;
                    case "30 minutes":
                        wallpaperSwitchTimerInterval = 30 * 60 * 1000;
                        break;
                    case "1 hour":
                        wallpaperSwitchTimerInterval = 1 * 60 * 60 * 1000;
                        break;
                    case "2 hours":
                        wallpaperSwitchTimerInterval = 2 * 60 * 60 * 1000;
                        break;
                    case "4 hours":
                        wallpaperSwitchTimerInterval = 4 * 60 * 60 * 1000;
                        break;
                    case "6 hours":
                        wallpaperSwitchTimerInterval = 6 * 60 * 60 * 1000;
                        break;
                    case "12 hours":
                        wallpaperSwitchTimerInterval = 12 * 60 * 60 * 1000;
                        break;
                }
                wallpaperSwitchTimer.Tick += new EventHandler(wallpaperSwitchTimer_Tick);

                if (wallpaperSwitchTimerInterval > 0)
                {
                    wallpaperSwitchTimer.Enabled = true;
                }
            }

            if (string.IsNullOrEmpty(wallpaperFileLocation) == false)
            {
                this.WindowState = FormWindowState.Minimized;

                this.ShowInTaskbar = false;

                this.Hide();
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
            }
        }

        private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem.Text == "Exit")
            {
                this.Close();
            }
        }

        private void wallpaperSearchTimer_Tick(object sender, EventArgs e)
        {
            wallpaperSearchTimer.Stop();

            wallpaperSearchTimer.Interval = wallpaperSearchTimerInterval;

            label6.Text = "Searching...";

            using (BackgroundWorker wallpaperSearchBackgroundWorker = new BackgroundWorker())
            {
                wallpaperSearchBackgroundWorker.DoWork += new DoWorkEventHandler(wallpaperSearchBackgroundWorker_DoWork);
                wallpaperSearchBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(wallpaperSearchBackgroundWorker_RunWorkerCompleted);
                wallpaperSearchBackgroundWorker.RunWorkerAsync("automatic");
            }
        }

        private struct wallpaperSearchResults
        {
            public string Origin
            {
                get;
                set;
            }

            public int DownloadedCount
            {
                get;
                set;
            }
        }

        private void wallpaperSearchBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            wallpaperSearchResults results = new wallpaperSearchResults();

            if (loggingEnabled == true)
            {
                Logging.LogMessage("Starting wallpaper search");
            }

            getWallpaperHashTable();

            int downloadedCount = 0;

            if (ConnectionExists() == true)
            {
                string[] countries = new string[] { "en-US", "en-UK", "en-AU", "en-NZ", "en-CA", "de-DE", "zh-CN", "ja-JP" };

                foreach (string country in countries)
                {
                    int currentIndex = 0;

                    bool moreImages = true;

                    while (moreImages)
                    {
                        WebRequest webRequest = WebRequest.Create(string.Format("{0}/HPImageArchive.aspx?format=xml&idx={1}&n=8&mkt={2}", url, currentIndex, country));

                        using (WebResponse webResponse = webRequest.GetResponse())
                        {
                            using (StreamReader streamReader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8))
                            {
                                string output = streamReader.ReadToEnd();

                                if (output.Length > 0 && output.Contains("<images>") == true)
                                {
                                    XmlDocument xmlDocument = new XmlDocument();

                                    try
                                    {
                                        xmlDocument.LoadXml(output);

                                        XmlNodeList xmlNodeList = xmlDocument.GetElementsByTagName("image");

                                        foreach (XmlNode xmlNode in xmlNodeList)
                                        {
                                            string fileurl = string.Format("{0}{1}", url, xmlNode.SelectSingleNode("url").InnerText);

                                            string tempfilename = Path.GetTempFileName();

                                            WebRequest fileWebRequest = WebRequest.Create(fileurl);

                                            using (WebResponse fileWebResponse = fileWebRequest.GetResponse())
                                            {
                                                using (Stream tempStream = File.Create(tempfilename))
                                                {
                                                    byte[] buffer = new byte[1024];
                                                    int bytesRead;

                                                    using (Stream fileStream = fileWebResponse.GetResponseStream())
                                                    {
                                                        do
                                                        {
                                                            bytesRead = fileStream.Read(buffer, 0, buffer.Length);

                                                            tempStream.Write(buffer, 0, bytesRead);
                                                        }
                                                        while (bytesRead > 0);
                                                    }
                                                }
                                            }

                                            string hash = HashValue.GetHash(tempfilename);

                                            if (wallpaperHashtable.ContainsValue(hash) == true)
                                            {
                                                File.Delete(tempfilename);
                                            }
                                            else
                                            {
                                                Regex regex = new Regex(String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()))), RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

                                                string name = xmlNode.SelectSingleNode("copyright").InnerText;

                                                int index = name.LastIndexOf(" -- ");
                                                if (index == -1)
                                                {
                                                    index = name.LastIndexOf(" – ");
                                                }

                                                name = name.Substring(0, index);
                                                name = regex.Replace(name, "_");
                                                if (translationLanguage != "Disabled")
                                                {
                                                    if (country.Substring(0, 2) != translationLanguage)
                                                    {
                                                        string from = country.Substring(0, 2);
                                                        if (from == "zh")
                                                        {
                                                            from += "-CHS";
                                                        }

                                                        name = Translator.Translate(appId, name, from, translationLanguage);
                                                    }
                                                }
                                                name += ".jpg";

                                                File.Move(tempfilename, Path.Combine(wallpaperFileLocation, name));

                                                wallpaperHashtable.Add(Path.Combine(wallpaperFileLocation, name), hash);

                                                if (loggingEnabled == true)
                                                {
                                                    Logging.LogMessage(string.Format("Found new wallpaper {0}", name));
                                                }

                                                downloadedCount++;
                                            }
                                        }

                                        if (xmlNodeList.Count < 8)
                                        {
                                            moreImages = false;
                                        }
                                        else
                                        {
                                            currentIndex += 8;
                                        }
                                    }
                                    catch
                                    {
                                        moreImages = false;
                                    }
                                }
                                else
                                {
                                    moreImages = false;
                                }
                            }
                        }
                    }
                }

                results.DownloadedCount = downloadedCount;
            }
            else
            {
                results.DownloadedCount = -1;
            }

            results.Origin = (string)e.Argument;

            e.Result = results;

            if (loggingEnabled == true)
            {
                Logging.LogMessage("Ending wallpaper search");
            }
        }

        private void wallpaperSearchBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            wallpaperSearchResults results = (wallpaperSearchResults)e.Result;

            if (results.DownloadedCount == -1)
            {
                notifyIcon.ShowBalloonTip(10 * 1000, Application.ProductName, string.Format("Unable to connect to {0}", url), ToolTipIcon.Error);

                wallpaperSearchTimer.Interval = 5 * 60 * 1000;
            }
            else if (results.DownloadedCount > 0)
            {
                label6.Text = string.Format("Last downloaded on {0}", DateTime.Now);

                notifyIcon.ShowBalloonTip(5 * 1000, Application.ProductName, label6.Text, ToolTipIcon.Info);

                getWallpaperList();

                loadListView();
            }
            else
            {
                label6.Text = string.Format("Last searched on {0}", DateTime.Now);
            }

            switch (results.Origin)
            {
                case "automatic":
                    wallpaperSearchTimer.Start();
                    break;
                case "manual":
                    linkLabel2.Enabled = true;
                    break;
            }
        }

        private void loadListView()
        {
            if (wallpaperList == null || wallpaperList.Count == 0)
            {
                getWallpaperList();
            }

            listView1.Items.Clear();

            ImageList imageList = new ImageList();
            imageList.ImageSize = new Size(96, 51);
            imageList.ColorDepth = ColorDepth.Depth16Bit;

            for (int i = 0; i < wallpaperList.Count; i++)
            {
                imageList.Images.Add(Image.FromFile(wallpaperList[i]));

                ListViewItem listViewItem = new ListViewItem(Path.GetFileNameWithoutExtension(wallpaperList[i]), i);
                listViewItem.Tag = wallpaperList[i];
                listViewItem.ToolTipText = string.Format("{0}\nDownloaded {1}", Path.GetFileNameWithoutExtension(wallpaperList[i]), File.GetLastWriteTime(wallpaperList[i]));

                listView1.Items.Add(listViewItem);
            }

            listView1.LargeImageList = imageList;

            GC.Collect();
        }

        private string url = "http://www.bing.com";

        private Hashtable wallpaperHashtable;

        private void getWallpaperHashTable()
        {
            if (loggingEnabled == true)
            {
                Logging.LogMessage("Starting hashing wallpapers");
            }

            wallpaperHashtable = new Hashtable();

            if (Directory.Exists(wallpaperFileLocation) == true)
            {
                string[] files = Directory.GetFiles(wallpaperFileLocation, "*.jpg");

                foreach (string file in files)
                {
                    wallpaperHashtable.Add(file, HashValue.GetHash(file));
                }
            }

            if (loggingEnabled == true)
            {
                Logging.LogMessage("Ending hashing wallpapers");
            }

        }

        private bool ConnectionExists()
        {
            try
            {
                Uri uri = new Uri(url);

                TcpClient tcpClient = new TcpClient(uri.DnsSafeHost, 80);

                tcpClient.Close();

                return true;
            }
            catch
            {
                return false;
            }
        }

        private List<string> wallpaperList;

        private void getWallpaperList()
        {
            if (Directory.Exists(wallpaperFileLocation) == true)
            {
                int wallpaperCount = 0;

                switch (wallpaperSwitchCount)
                {
                    case "10 images":
                        wallpaperCount = 10;
                        break;
                    case "25 images":
                        wallpaperCount = 25;
                        break;
                    case "50 images":
                        wallpaperCount = 50;
                        break;
                    case "100 images":
                        wallpaperCount = 100;
                        break;
                    case "All images":
                        wallpaperCount = int.MaxValue;
                        break;
                }

                wallpaperList = new List<string>(wallpaperCount == int.MaxValue ? 0 : wallpaperCount);

                string[] files = Directory.GetFiles(wallpaperFileLocation, "*.jpg");

                switch (wallpaperSwitchMethod)
                {
                    case "With the latest":
                        Array.Sort(files, new SortFiles());

                        Array.Reverse(files);

                        for (int i = 0; i < Math.Min(files.Length, wallpaperCount); i++)
                        {
                            wallpaperList.Add(files[i]);
                        }

                        break;
                    case "With random":
                        Random random = new Random();

                        while (wallpaperList.Count <= wallpaperCount)
                        {
                            wallpaperList.Add(files[random.Next(files.Length - 1)]);
                        }

                        break;
                }
            }
            else
            {
                wallpaperList = new List<string>();
            }

            GC.Collect();
        }

        private void wallpaperSwitchTimer_Tick(object sender, EventArgs e)
        {
            wallpaperSwitchTimer.Stop();

            wallpaperSwitchTimer.Interval = wallpaperSwitchTimerInterval;

            if (RDP.IsRemote() == false && Monitor.IsActive() == true)
            {
                wallpaperSwitch();
            }
            else
            {
                if (loggingEnabled == true)
                {
                    Logging.LogMessage("Not switching due to RDP or monitor standby");
                }
            }

            wallpaperSwitchTimer.Start();
        }

        private void wallpaperSwitch()
        {
            if (wallpaperList == null || wallpaperList.Count == 0)
            {
                getWallpaperList();
            }

            if (wallpaperList.Count > 0)
            {
                string desktopBackground = wallpaperList[new Random().Next(wallpaperList.Count - 1)];

                if (loggingEnabled == true)
                {
                    Logging.LogMessage(string.Format("Switching wallpaper {0}", desktopBackground));
                }

                DesktopWallpaper.SetDesktopBackground(desktopBackground, desktopBackgroundStyle);
            }

            GC.Collect();
        }

        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            this.Show();

            this.WindowState = FormWindowState.Normal;

            this.ShowInTaskbar = true;

            this.Activate();
        }

        private void textBox1_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
            {
                if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
                {
                    textBox1.Text = folderBrowserDialog.SelectedPath;
                }
            }
        }

        private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (textBox1.Text != string.Empty)
            {
                linkLabel2.Enabled = false;

                label6.Text = "Searching...";

                using (BackgroundWorker wallpaperSearchBackgroundWorker = new BackgroundWorker())
                {
                    wallpaperSearchBackgroundWorker.DoWork += new DoWorkEventHandler(wallpaperSearchBackgroundWorker_DoWork);
                    wallpaperSearchBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(wallpaperSearchBackgroundWorker_RunWorkerCompleted);
                    wallpaperSearchBackgroundWorker.RunWorkerAsync("manual");
                }
            }
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (textBox1.Text != string.Empty && comboBox1.SelectedItem != null && comboBox2.SelectedItem != null && comboBox5.SelectedItem != null && comboBox3.SelectedItem != null && comboBox4.SelectedItem != null && comboBox5.SelectedItem != null && comboBox6.SelectedItem != null)
            {
                Properties.Settings.Default.wallpaperFileLocation = textBox1.Text;
                Properties.Settings.Default.wallpaperSearchFrequency = Convert.ToString(comboBox1.SelectedItem);
                Properties.Settings.Default.wallpaperSwitchFrequency = Convert.ToString(comboBox2.SelectedItem);
                Properties.Settings.Default.wallpaperSwitchMethod = Convert.ToString(comboBox3.SelectedItem);
                Properties.Settings.Default.wallpaperSwitchCount = Convert.ToString(comboBox4.SelectedItem);
                Properties.Settings.Default.desktopBackgroundStyle = Convert.ToString(comboBox5.SelectedItem);
                if (comboBox6.SelectedItem.GetType().ToString() == "System.Globalization.CultureInfo")
                {
                    Properties.Settings.Default.translationLanguage = Convert.ToString(((CultureInfo)comboBox6.SelectedItem).Name);
                }
                else
                {
                    Properties.Settings.Default.translationLanguage = Convert.ToString(comboBox6.SelectedItem);
                }
                Properties.Settings.Default.loggingEnabled = Convert.ToString(checkBox1.Checked);

                Properties.Settings.Default.Save();

                if (loggingEnabled == true)
                {
                    Logging.LogMessage("Saving settings");
                }

                MessageBox.Show("Complete, the application will now restart.", "Save", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                Application.Restart();
            }
            else
            {
                MessageBox.Show("All fields are required.", "Save", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            if (loggingEnabled == true)
            {
                Logging.LogMessage(string.Format("Switching wallpaper {0}", Convert.ToString(listView1.SelectedItems[0].Tag)));
            }

            DesktopWallpaper.SetDesktopBackground(Convert.ToString(listView1.SelectedItems[0].Tag), desktopBackgroundStyle);
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            wallpaperSearchFrequency = Convert.ToString(comboBox1.SelectedItem);
        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            wallpaperSwitchFrequency = Convert.ToString(comboBox2.SelectedItem);
        }

        private void comboBox34_SelectedIndexChanged(object sender, EventArgs e)
        {
            wallpaperSwitchMethod = Convert.ToString(comboBox3.SelectedItem);
            wallpaperSwitchCount = Convert.ToString(comboBox4.SelectedItem);

            getWallpaperList();

            loadListView();
        }

        private void comboBox5_SelectedIndexChanged(object sender, EventArgs e)
        {
            desktopBackgroundStyle = Convert.ToString(comboBox5.SelectedItem);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            loggingEnabled = checkBox1.Checked;
        }

        private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            ClickOnce.CheckForUpdate();
        }

        private void linkLabel4_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (File.Exists(Application.ProductName + ".txt"))
            {
                System.Diagnostics.Process.Start(Application.ProductName + ".txt");
            }
            else
            {
                MessageBox.Show("No log file exists.", "View Log File", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}